package com.xinyou.u8c.utils;

import com.xinyou.u8c.entity.Bfaccountingdepartmentzjb;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/** 采用apache commons cvs
 *导出csv文件，设定每个文件的最大数量，多个csv文件打成zip包
 * 类说明
 */
@Slf4j
public class CsvUtils {
    
    private static final int BUFFER_SIZE = 8192;//缓冲区大小

    /**
     * 打成压缩多个文件
     * @param downloadTempPath 参考：request.getServletContext().getRealPath("download")
     * @param relativePath 举例：${sessionId}/${productId}
     * @param fileName 不带后缀名，举例：多个文件返回文件的后缀是zip，单个是csv
     * @return 服务器磁盘上的文件
     * @throws IOException 写入失败
     */
    public static File writeToZip(String downloadTempPath, String relativePath,String fileName,List<File> files) throws IOException {
        if(StringUtils.isBlank(fileName)) {
            throw new IOException("压缩包名为空，生成压缩包失败");
        }
        String tempPath = downloadTempPath;
        if(StringUtils.isBlank(tempPath)) {
            tempPath = "/salemanagement/temp/";
        }
        if(!StringUtils.endsWith(tempPath, "/")) {
            tempPath += "/";
        }
        if(relativePath == null) {
            relativePath = "";
        }
        relativePath = StringUtils.removeStart(relativePath, "/");//如果以"/"开头，则去掉"/"字符
        String absolutePath = tempPath + relativePath;
        return zip(fileName + ".zip", absolutePath, files.toArray(new File[files.size()]));
    }

    /**
     * 将内容写入csv文件
     * @param downloadTempPath 参考：request.getServletContext().getRealPath("download")
     * @param relativePath 举例：${sessionId}/${productId}
     * @param fileName 不带后缀名，举例：多个文件返回文件的后缀是zip，单个是csv
     * @param csvCharset 读写csv文件的编码字符集
     * @param header 表头
     * @param records 内容
     * @return 服务器磁盘上的文件
     * @throws IOException 写入失败
     */
    public static File writeToCsv(String downloadTempPath, String relativePath, String fileName, String csvCharset, List<String> header, List<List<String>> records) throws IOException {
        if(StringUtils.isBlank(fileName)) {
            throw new IOException("文件名为空，生成csv文件失败");
        }
        if(header == null || header.isEmpty()) {
            throw new IOException("表头为空，生成csv文件[" + fileName + "]失败");
        }

        String tempPath = downloadTempPath;
        if(StringUtils.isBlank(tempPath)) {
            tempPath = "/salemanagement/temp/";
        }
        if(!StringUtils.endsWith(tempPath, "/")) {
            tempPath += "/";
        }
        if(relativePath == null) {
            relativePath = "";
        }
        relativePath = StringUtils.removeStart(relativePath, "/");//如果以"/"开头，则去掉"/"字符
        String absolutePath = tempPath + relativePath;

        int recordsNum = records == null ? 0 : records.size();
        List<List<String>> csvData = new ArrayList<List<String>>();
        csvData.add(header);
        if(recordsNum > 0) {//如果有数据则添加，否则将返回一个只有表头的csv文件
            csvData.addAll(records);
        }
        return writeRecords(csvData, absolutePath, fileName + ".csv", false, csvCharset);
    }

    /**
     * 将字符串List存到文件中，每个List<String>一行
     * @param records 字符串List
     * @param filePath 目标全路径
     * @param fileName 目标文件名
     * @param append true追加，false覆盖
     * @param charset 编码字符集
     * @return 目标文件
     * @throws IOException 写文件出错(包括字符集错误)
     */
    public static <CSVPrinter> File writeRecords(List<List<String>> records, String filePath, String fileName, boolean append, String charset) throws IOException {
        File parentFile = new File(filePath);
        if(!parentFile.exists()) {
            parentFile.mkdirs();
        }

        OutputStreamWriter osw = null;
        org.apache.commons.csv.CSVPrinter csvPrinter = null;
        try {
            File file = new File(parentFile, fileName);
            if(StringUtils.isBlank(charset)) {
                    osw = new OutputStreamWriter(new FileOutputStream(file, append));
            } else {
                osw = new OutputStreamWriter(new FileOutputStream(file, append), charset);
            }
            csvPrinter = new org.apache.commons.csv.CSVPrinter(osw, CSVFormat.DEFAULT);
            for (int i = 0; i < records.size(); i++) {
                csvPrinter.printRecord(records.get(i));
            }
            return file;
        } finally {
            if(csvPrinter!=null) {
                try {
                    csvPrinter.close();
                } catch (Exception e) {
                    log.warn("关闭CSVPrinter失败", e);
                }
            }
            if(osw!=null) {
                try {
                    osw.close();
                } catch (Exception e) {
                    log.warn("关闭csv文件[" + fileName + "]失败", e);
                }
            }
        }
    }

    /**
     * 压缩文件(保留文件的修改时间，不保留文件夹的)
     * @param targetFileName 不包含路径，以zip结尾
     * @param absRootPath 源文件必须在这个目录或子目录下，目标文件生成在这个目录下
     */
    public static File zip(String targetFileName, String absRootPath, File... sourceFiles) throws IOException {
        File targetFile = new File(absRootPath, targetFileName);
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(new FileOutputStream(targetFile));
            compress(zos, absRootPath, sourceFiles);
        } finally {
            if(zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    log.warn("关闭目标文件[" + targetFile + "]的输入流失败", e);
                }
            }
        }
        return targetFile;
    }
    private static void compress(ZipOutputStream zipOutputStream, String absRootPath, File... sourceFiles) throws IOException {
        absRootPath = StringUtils.replace(absRootPath, "\\", "/");
        if(!absRootPath.endsWith("/")) {
            absRootPath += "/";
        }
        for (File sourceFile : sourceFiles) {
            if (sourceFile.isFile()) {
                String absPath = sourceFile.getAbsolutePath();
                absPath = StringUtils.replace(absPath, "\\", "/");
                String zipEntryName = StringUtils.removeStart(absPath, absRootPath);
                if(absPath.equals(zipEntryName)) {
                    throw new RuntimeException("压缩出错，absRootPath["+absRootPath+"]与absPath["+absPath+"]不匹配");
                }
                ZipEntry zipEntry = new ZipEntry(zipEntryName);
//				zipEntry.setSize(sourceFile.length());
                zipEntry.setTime(sourceFile.lastModified());
                zipOutputStream.putNextEntry(zipEntry);

                InputStream bis = null;
                try {
                    bis = new BufferedInputStream(new FileInputStream(sourceFile));
                    int readLength = -1;
                    byte[] buffer = new byte[BUFFER_SIZE];
                    while ((readLength = bis.read(buffer, 0, BUFFER_SIZE)) != -1) {
                        zipOutputStream.write(buffer, 0, readLength);
                    }
                } finally {
                    if(bis != null) {
                        try {
                            bis.close();
                        } catch (IOException e) {
                            log.warn("关闭文件[" + sourceFile + "]的输入流失败", e);
                        }
                    }
                    try {
                        zipOutputStream.closeEntry();
                    } catch (Exception e) {
                        log.warn("关闭ZipEntry[" + zipEntryName + "]失败", e);
                    }
                }
            } else {
                compress(zipOutputStream, absRootPath, sourceFile.listFiles());
            }
        }
    }

    /**
     * 解压文件(保留文件的修改时间，不保留文件夹的)
     * @param targetDir 目标目录的绝对路径
     */
    public static void unzip(File zipFile, String targetDir) throws IOException {
        targetDir = StringUtils.replace(targetDir, "\\", "/");
        if(!targetDir.endsWith("/")) {
            targetDir += "/";
        }
        ZipFile zip = null;
        try {
            zip = new ZipFile(zipFile);
            Enumeration<? extends ZipEntry> entries = zip.entries();
            while(entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                if(!entry.isDirectory()) {//加压文件会自动创建父目录，文件夹全部忽略
                    File entryFile = new File(targetDir + entry.getName());
                    if (entryFile.exists()) {
                        entryFile.delete();
                    }
                    File parent = entryFile.getParentFile();
                    if(!parent.exists()) {
                        parent.mkdirs();
                    }
                    InputStream bis = null;
                    OutputStream bos = null;
                    try {
                        bis = new BufferedInputStream(zip.getInputStream(entry));
                        bos = new BufferedOutputStream(new FileOutputStream(entryFile));
                        int readLength = -1;
                        byte[] buffer = new byte[BUFFER_SIZE];
                        while ((readLength = bis.read(buffer, 0, BUFFER_SIZE)) != -1) {
                            bos.write(buffer, 0, readLength);
                        }
                        bos.flush();
                    } finally {
                        if(bis != null) {
                            try {
                                bis.close();
                            } catch (IOException e) {
                                log.warn("关闭entry[" + entry.getName() + "]的输入流失败", e);
                            }
                        }
                        if(bos != null) {
                            try {
                                bos.close();
                            } catch (IOException e) {
                                log.warn("关闭文件[" + entryFile.getPath() + "]的输出流失败", e);
                            }
                        }
                    }
                    entryFile.setLastModified(entry.getTime());
                }
            }
        } finally {
            if(zip != null) {
                try {
                    zip.close();
                } catch (IOException e) {
                    log.warn("关闭压缩文件[" + zipFile.getPath() + "]失败", e);
                }
            }
        }
    }
}

